Lambda表达式
匿名函数有函数体,但没有函数名。
匿名函数是很多高级语言都支持的概念,如lisp语言在1958年首先采用匿名函数。正因为如此,C++11也同样引入了lambda函数。
在C++11中,你可以在源码中内联一个lambda函数,这就使得创建快速的、一次性的函数变得简单了。
相同类似功能我们也可以使用函数对象或者函数指针实现:函数对象能维护状态,但语法开销大,而函数指针语法开销小,却没法保存范围内的状态。lambda表达式正是结合了两者的优点。
声明Lambda表达式
1 | [capture list] (params list) mutable exception-> return type { function body }; |
- capture list:捕获外部变量列表
- params list:形参列表
- mutable指示符:用来说用是否可以修改捕获的变量
- exception:异常设定
- return type:返回类型
function body:函数体
标号1. 函数声明了一个const类型的表达式,此声明不可改变capture list中的捕获的值。
标号2. 函数省略了返回值,此时如果function body内含有return语句,则按return语句返回类型决定返回值类型,若无则返回值为void类型。
标号3. 函数无参数列表,意味无参函数。简单的例子
在C++中我们对STL库中的sort()运用十分频繁,接下来就是关于他的一个例子:
1 | //省略细节 |
接下来是lambda表达式的形式:1
2
3
4
5{
vector<int> vec{1,0,9,5,3,3,7,8,2};
sort(lbvec.begin(), lbvec.end(), [](int a, int b) -> bool { return a < b; });
// Lambda表达式
}
lambda不仅提高了代码可读性,且在例子中,这一个提供判断依据的函数是只需调用一次,在这时,lambda表达式就显示出它的“即用即扔”的特点,很适合这种不需要重复调用且运用区域单一的情景(而不是去多写一个compare的函数)。
捕获外部变量
Lambda表达式可以捕获外面变量,但需要我们提供一个谓词函数([capture list]在声明表达式最前)
类似参数传递方式:值传递、引入传递、指针传递。
在Lambda表达式中,外部变量捕获方式也类似:值捕获、引用捕获、隐式捕获。
值捕获
1 | int a = 123; |
值捕获和参数传递中的值传递类似,被捕获的值在Lambda表达式创建时通过值拷贝的方式传入,因此Lambda表达式函数体中不能修改该外部变量的值;同样,函数体外对于值的修改也不会改变被捕获的值。
引用捕获
1 | int a = 123; |
引用捕获的变量使用的实际上就是该引用所绑定的对象,因此引用对象的改变会改变函数体内对该对象的引用的值。
隐式捕获
隐式捕获有两种方式,分别是
[=]:以值补获的方式捕获外部所有变量
[&]:表示以引用捕获的方式捕获外部所有变量。1
2
3int a = 123 ,b=321;
auto df = [=] { cout << a << b << endl; }; // 值捕获
auto rf = [&] { cout << a << b << endl; }; // 引用捕获
其他捕获方式
C++11Lambda表达式捕获外部变量形式 | |
---|---|
[ ] | 不捕获任何变量(无参函数) |
[变量1,&变量2, …] | 值(引用)形式捕获指定的多个外部变量 |
[this] | 值捕获this指针 |
[=, &x] | 变量x以引用形式捕获,其余变量以传值形式捕获 |
Lambda表达式的参数
- 参数列表中不能有默认参数
- 不支持可变参数
- 所有参数必须有参数名